__author__ = 'maikel'

from utilsP4 import *
import random
import math
import numpy as np


class AdWin:
    """
        Finestra lliscant adaptativa que permet trobar la mitja de totes les dades que es
        van inserin i que permet trobar canvis bruscos
        en elles i adaptar-se a ells a partir d'un valor de confianca indicat per l'usuari.
    """
    def __init__(self, d):
        """
            Constructor de la finestra.

            param d Confianca el canvi. Com mes gran mes facil sera detectar un canvi.
        """
        self.data = []
        self.length = 0
        self.rel = d
        self.m = 0.0

    def ecut(self, w1, w2):
        """
            Calculo epsilon, segun la cota de Hoeffding, que nos asegura que la evidencia sobre la
            diferencia entre las medias es suficiente segun el numero de puntos de cada ventana.
        """
        m = 1.0 / ( (1.0/len(w1)) + (1.0/len(w2)) )
        rel_prima = self.rel / float(len(w1) + len(w2))
        epsilon = math.sqrt( (1.0/(2.0*m)) * math.log(4.0/rel_prima) )
        return math.fabs(np.mean(w1) - np.mean(w2)) > epsilon

    def add(self, x_t):
        """
            Afegeix una nova dada a la finestra i despres de comprovar si hi ha hagut un canvi
            actualitza les dades necessaries.

            param x_t Nova dada.

            return self.m Mitja de les dades del interior de la finestra
            return 	0 si no hi ha hagut cap canvi significatiu
                    1 si hi ha hagut un canvi i la mitja ha disminuit
                    -1 si hi ha hagut un canvi i la mitja ha augmentat
        """
        self.data.append(x_t)
        self.length += 1

        canvi = 0
        idx = 1
        while idx < len(self.data):
            w1 = self.data[:idx]
            w2 = self.data[idx:]

            if self.ecut(w1, w2):
                self.data = w2
                canvi = 2
                self.length -= len(w1)

            idx += 1
        mean = np.mean(self.data)
        if canvi == 2:
            if self.m > mean:
                canvi = 1
            elif self.m < mean:
                canvi = -1

        self.m = mean

        return mean, canvi

def f1():
    v = []
    for i in xrange(100):
        v.append(random.randint(0, 1))
    for i in xrange(200):
        v.append(random.randint(3, 4))

    output1 = []
    output2 = []
    method1 = AdWin(0.95)
    for item in v:
        mean, action = method1.add(item)
        output1.append(mean)
        output2.append(action)

    pylab.plot(v)
    pylab.plot(output1, 'r')
    pylab.plot(output2, 'y')
    pylab.show()


def f2():
    # PROVA AMB DADES SINTETIQUES

    # Adatptative window amb d=0.98
    method1 = AdWin(0.95)

    v = [] # Genera un mostra de 400 + 600 valors amb un canvi brusc entre 400 i 401.
    for i in xrange(200):
        v.append(0.6 + 0.1 * (random.random() - 0.5))
    for i in xrange(400):
        v.append(0.4 + 0.1 * (random.random() - 0.5))
    for i in xrange(600):
        v.append(0.1 * (random.random() - 0.5))

    # Anem afegint de forma sequencial les dades dins la nostra finestra lliscant. Guardem el resultat dins la llista output1 i output2
    output1 = []
    for item in v:
        mean, action = method1.add(item)
        output1.append(mean)


    # Visualitzem el resultat
    pylab.plot(v)
    pylab.plot(output1, 'r') # plot del resultat del Metode 1
    pylab.show()

#f1()
f2()